__author__ = 'cain76'

import os, sys
sys.path.insert(0, os.path.expanduser('~') + '/lib/python')

from mylib.mygl import *
from mylib.gl3d.light import *
from mylib.gl3d.texture import *
from mylib.gl3d.heightmap import *
from mylib.gl3d.camera import *
from mylib.gl3d.shader import *
from mylib.gl3d.drawobject import *
from mylib.gl3d.obj import *
from mylib.gl3d.bullet import *
from mylib.game.maze import *
fogColor = (0.8, 0.8, 0.8, 1.0)  # // Fog Colour
SCREEN_SIZE = (1280, 800)


def gl_init():  # We call this right after our OpenGL window is created.
    global fogColor

    glEnable(GL_TEXTURE_2D)  # // Enable Texture Mapping
    glEnable(GL_LIGHTING)
    glEnable(GL_COLOR_MATERIAL)

    glClearColor(0.0, 0.0, 0.0, 1.0)  # // Black Background
    glClearDepth(1.0)  # // Depth Buffer Setup
    glDepthFunc(GL_LEQUAL)  # // The Type Of Depth Testing
    glEnable(GL_DEPTH_TEST)  # // Enable Depth Testing
    glShadeModel(GL_SMOOTH)  # // Select Smooth Shading
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)  # // Set Perspective Calculations To Most Accurate

    # glPolygonMode(GL_FRONT, GL_FILL)
    # glPolygonMode(GL_BACK, GL_LINE)
    '''
    fogfilter=0
    fogMode=[GL_EXP, GL_EXP2, GL_LINEAR]
    glFogi(GL_FOG_MODE, fogMode[fogfilter])  # // Fog Mode
    glFogfv(GL_FOG_COLOR, fogColor)  # // Set Fog Color
    glFogf(GL_FOG_DENSITY, 0.1)  # // How Dense Will The Fog Be
    glHint(GL_FOG_HINT, GL_NICEST)  # // Fog Hint Value
    glFogf(GL_FOG_START, 10.0)  # // Fog Start Depth
    glFogf(GL_FOG_END, 50.0)  # // Fog End Depth
    glEnable(GL_FOG)  # // Enables GL_FOG
    # // Set Up Fog
    glEnable(GL_FOG)  # // Enable Fog
    glFogi(GL_FOG_MODE, GL_LINEAR)  # // Fog Fade Is Linear
    glFogfv(GL_FOG_COLOR, fogColor)  # // Set The Fog Color
    glFogf(GL_FOG_START, 10.0)  # // Set The Fog Start
    glFogf(GL_FOG_END, 100.0)  # // Set The Fog End
    glHint(GL_FOG_HINT, GL_NICEST)  # // Per-Pixel Fog Calculation
    glFogi(GL_FOG_COORDINATE_SOURCE_EXT, GL_FOG_COORDINATE_EXT)  # // Set Fog Based On Vertice Coordinates
    # glInitFogCoordEXT()
    # glFogCoordPointer
    '''
    return True  # // Return TRUE (Initialization Successful)


# The function called when our window is resized (which shouldn't happen if you enable fullscreen, below)
def gl_resize(width, height):
    if height == 0:  # Prevent A Divide By Zero If The Window Is Too Small
        height = 1

    glViewport(0, 0, width, height)  # Reset The Current Viewport And Perspective Transformation
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    # // field of view, aspect ratio, near and far
    # This will squash and stretch our objects as the window is resized.
    gluPerspective(45.0, float(width) / float(height), 0.1, 5000.0)

    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()


def print_camera_info(cam):
    print('rotate = %5.2f, angle = %5.2f, distance = %8.3f, '
          'camera = (%5.2f,%5.2f,%5.2f), target = (%5.2f,%5.2f,%5.2f)' %
          (cam.rotate, cam.angle, cam.distance,
           cam.camera.x, cam.camera.y, cam.camera.z,
           cam.target.x, cam.target.y, cam.target.z))


def get_light_pos(cam):
    dist = cam.distance * 0.5
    p1 = cam.get_position(cam.camera, distance=dist)
    # p1 = cam.camera

    d = Vec3d()
    d.x = -math.sin(math.radians(cam.rotate + 90))
    d.y = -math.tan(math.radians(cam.angle))
    d.z = math.cos(math.radians(cam.rotate + 90))
    '''
    d.x = -math.sin(math.radians(cam.rotate + 90))
    d.y = -math.tan(math.radians(cam.angle))
    d.z = math.cos(math.radians(cam.rotate + 90))
    '''
    return p1, d


def get_shader_src():
    return """
    vec3 ambient_light = light_ambient(light1).rgb+
                         light_ambient(light2).rgb+
                         light_ambient(light3).rgb+
                         light_ambient(light4).rgb+
                         light_ambient(light5).rgb+
                         light_ambient(light6).rgb+
                         light_ambient(light7).rgb+
                         light_ambient(light8).rgb;

    vec3 diffuse_light = light_diffuse(light1).rgb*light_att(light1)+
                     2.0*light_diffuse(light2).rgb*light_att(light2)*light_spot(light2)+
                         light_diffuse(light3).rgb*light_att(light3)+
                     2.0*light_diffuse(light4).rgb*light_att(light4)*light_spot(light4)+
                         light_diffuse(light5).rgb*light_att(light5)+
                     2.0*light_diffuse(light6).rgb*light_att(light6)*light_spot(light6)+
                         light_diffuse(light7).rgb*light_att(light7)+
                     2.0*light_diffuse(light8).rgb*light_att(light8)*light_spot(light8);

    vec3 specular_light = light_specular_ph(light1).rgb*light_att(light1)+
                      2.0*light_specular_ph(light2).rgb*light_att(light2)*light_spot(light2)+
                          light_specular_ph(light3).rgb*light_att(light3)+
                      2.0*light_specular_ph(light4).rgb*light_att(light4)*light_spot(light4)+
                          light_specular_ph(light5).rgb*light_att(light5)+
                      2.0*light_specular_ph(light6).rgb*light_att(light6)*light_spot(light6)+
                          light_specular_ph(light7).rgb*light_att(light7)+
                      2.0*light_specular_ph(light8).rgb*light_att(light8)*light_spot(light8);

    color.rgb += ambient_color.rgb*ambient_light;
    color.rgb += diffuse_color.rgb*diffuse_light;
    color.rgb += specular_color.rgb*specular_light;
    color.rgb *= texture2D(tex2D_1,uv).rgb; // texture color
    """

# 4.0 36.0 0 14
def generate_map(map_file=None):
    spx, spy, epx, epy = 0, 0, 0, 0
    hmap = HeightMap(HeightMap.DT_VBO)
    # height_map = HeightMap(HeightMap.DT_DL)
    if map_file == HeightMap.MT_MAZE:
        mw, mh = 16, 16
        m, spx, spy, epx, epy = gen_maze(mw, mh)
        hmap.make_maze(m, mw, mh, 8)
        spx = (spx - mw / 2) * 8 + 4
        spy = (spy - mh / 2) * 8 + 4
        print(spx, spy, epx, epy)
    elif map_file == HeightMap.MT_RANDOM:
        hmap.make_random(128, 128, 25, 25)
    else:
        if map_file is not None:
            hmap.load(map_file)
        else:
            return None
    hmap.generate3(f_resolution=2.0, f_height_scale=1, f_size_scale=1.0)
    return hmap, spx / 2.0, spy / 2.0, epx, epy

is_mouse_dragging = 0
mouse_drag_last_pos = 0, 0


def mouse_move_event(event, camera):
    global is_mouse_dragging, mouse_drag_last_pos

    if event.type == MOUSEMOTION:  # {
        if is_mouse_dragging in (1, 3):  # {
            mx, my = event.pos
            lx, ly = mouse_drag_last_pos
            dx, dy = (mx - lx), (my - ly)
            mouse_drag_last_pos = mx, my
            if is_mouse_dragging == 3: camera.change_view(dy / 5.0, dx / 5.0)
            elif is_mouse_dragging == 1: camera.move(_x=dx, _z=dy)
            print_camera_info(camera)
        # }
    # }
    if event.type == MOUSEBUTTONDOWN:  # {
        if event.button in (1, 3):  # {
            mouse_drag_last_pos = event.pos
            is_mouse_dragging = event.button
        # }
        if event.button == 4: camera.distance *= 0.8
        if event.button == 5: camera.distance *= 1.2
        print_camera_info(camera)
    # }
    if event.type == MOUSEBUTTONUP:
        if event.button in (1, 3): is_mouse_dragging = 0


def run():
    usr_home = os.path.expanduser('~')
    data_home = usr_home + '/src/res/'

    pygame.init()
    pygame.display.set_mode(SCREEN_SIZE, HWSURFACE | OPENGL | DOUBLEBUF)
    # gl_ext_import_all()
    gl_init()
    gl_resize(SCREEN_SIZE[0], SCREEN_SIZE[1])

    bullet = []
    # // Load The Mesh Data

    texfile = None
    # mapfile = 'heightmap/box1.bmp'
    # mapfile = 'ammoc01.jpg'
    # mapfile = "heightmap/box.bmp"
    # mapfile = "heightmap/H002.bmp"
    mapfile = "heightmap/texture.jpg"
    # mapfile = "heightmap/H005.bmp"
    # mapfile = "heightmap/Earth.bmp"
    # mapfile = "heightmap/H000.bmp"
    if texfile is None: texfile = mapfile

    textures = Textures()
    textures.load('tex', data_home + texfile)

    # MT_MAZE = 'generate maze map'
    # MT_IMAGE = 'read from image'
    # MT_RANDOM = 'generate random map'

    height_map = None
    height_map, start_x, start_y, _, _ = generate_map(HeightMap.MT_MAZE)

    obj = None
    # obj = OBJ(os.path.expanduser('~') + '/src/res/plane.obj', swapyz=True)

    light1 = None
    light2 = None
    '''
    light1 = Light3d(1)
    light1.set_type(MYGL_POINT_LIGHT)
    # light.set_type(MYGL_DIRECTIONAL_LIGHT)
    light1.set_pos([0.0, 500.0, 500.0])
    # light1.set_spot_dir([0.0, -1.0, 0.0])
    light1.set_diffuse([1.0, 1.0, 1.0])
    light1.enable()
    '''
    light2 = Light3d(2)
    light2.set_type(MYGL_POINT_LIGHT)
    # light2.set_type(MYGL_DIRECTIONAL_LIGHT)

    light2.set_ambient([0, 0, 0.1])
    light2.set_diffuse([0.9, 0.9, 0.9])
    light2.set_specular([1, 1, 1])
    light2.set_atten(1.0, 0.1, 0.0)
    light2.set_spot_angle(45.0)
    light2.set_spot_ex(0.2)
    light2.enable()

    Shader = GLShader()
    Shader.render_equation(get_shader_src())
    errors = Shader.compile()
    print('Shader Load Check - ', errors)

    camera = Camera(Camera.CT_CAMERA)
    camera.set_view_pos(_x=start_x, _z=start_y)
    camera.distance = 10.0
    camera.angle = 0.0

    do = DrawObject()
    clock = pygame.time.Clock()
    prev_height = height_map.minimum
    while True:
        for event in pygame.event.get():
            if event.type == QUIT: return
            if event.type == KEYUP and event.key == K_ESCAPE: return
            mouse_move_event(event, camera)
            '''
            if event.type == KEYDOWN and event.key == K_SPACE:
                bullet.append(Bullet(camera.get_view_pos(), camera.get_view_vector()))
            if event.type == KEYDOWN and event.key == K_UP:
            if event.type == KEYDOWN and event.key == K_LEFT:
            if event.type == KEYDOWN and event.key == K_RIGHT:
            '''
        # Clear the screen, and z-buffer
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        # Reset The Modelview Matrix
        glLoadIdentity()
        # position, zoom
        time_passed = clock.tick(29)

        time_passed_seconds = time_passed / 1000.
        move_speed = time_passed_seconds * 10.0
        pressed = pygame.key.get_pressed()
        move_x = 0
        move_z = 0
        move_a = 0
        move_r = 0
        if pressed[K_q]: move_x = move_speed
        if pressed[K_e]: move_x = -move_speed
        if pressed[K_w]: move_z = move_speed
        if pressed[K_s]: move_z = -move_speed
        if pressed[K_r]: move_a = -move_speed
        if pressed[K_f]: move_a = move_speed
        if pressed[K_a]: move_r = -move_speed
        if pressed[K_d]: move_r = move_speed

        if move_a != 0 or move_r != 0:
            camera.change_view(_a=move_a, _r=move_r)
        if move_x != 0 or move_z != 0:
            camera.move(_x=move_x, _z=move_z)
        if height_map is not None:
            cy = height_map.get_height(camera.get_view_pos().x, camera.get_view_pos().z)
            if abs(prev_height - cy) > 2 and prev_height > height_map.minimum:
                camera.move(_x=-move_x, _z=-move_z)
            else:
                prev_height = cy
                camera.set_view_pos(_y=cy + 0.2)
        p1, p2 = get_light_pos(camera)
        camera.look()
        if light1 is not None: light1.set()
        if light2 is not None:
            light2.set_pos([p1.x, p1.y, p1.z])
            '''
            _, time_angle = divmod(floor(time_angle + time_passed * 0.1), 360.0)
            _lx, _ly, _lz = 0.0, -1.0, 0.0
            _lx = sin(radians(time_angle))
            _lz = cos(radians(time_angle))
            light2.set_spot_dir([_lx, _ly, _lz])
            '''
            light2.set_spot_dir([p2.x, p2.y, p2.z])
            light2.set()

        glLibUseShader(Shader)

        Shader.pass_texture(textures.get('tex')[0])
        textures.bind('tex')

        material_set(MYGL_MATERIAL_DEFAULT)
        if height_map is not None:
            height_map.render()
            # height_map.render_faces(camera.get_view_pos(), 25)
        if obj is not None: glCallList(obj.gl_list)

        glLibUseShader(None)

        glColor3f(0.5, 0.5, 0.5)
        do.glDrawCube(0.0, 0.0, 0.0, size=600.0)

        glColor3f(0.0, 1.0, 0.0)
        do.set_pos(camera.target)
        do.glDrawCube(camera.target.x, camera.target.y, camera.target.z, size=0.5)

        glColor3f(1.0, 0.0, 0.0)
        do.set_pos(p1)
        do.glDrawCube(p1.x, p1.y, p1.z, size=0.1)
        # do.set_pos(camera.camera)
        # do.gltDrawSphere(_size=0.5)

        if light1 is not None: light1.draw_as_point()
        if light2 is not None: light2.draw_as_point()
        # Show the screen
        pygame.display.flip()
    textures.clear()


if __name__ == '__main__':
    run()
